package pl.elka.pobr.ppr.logic.recognition;

import java.util.Collections;
import java.util.Iterator;
import java.util.Vector;

import pl.elka.pobr.ppr.logic.Coord;
import pl.elka.pobr.ppr.utils.Log;

public class Segmentator {

    private static final String LOG_TAG = "Segmentator";
    private Coord[][] mAllPixelsCoords;
    private boolean[][] mAllPixelsBoolVals;
    private Vector<Segment> mSegments;
    private Vector<Integer> mSegmentIdsToRemove;
    private int w;
    private int h;

    public Segmentator(boolean[][] pixels) {
        w = pixels.length;
        h = pixels[0].length;
        mAllPixelsBoolVals = pixels;
        mAllPixelsCoords = new Coord[w][h];
        for (int y = 0; y < h; ++y) {
            for (int x = 0; x < w; ++x) {
                mAllPixelsCoords[x][y] = new Coord(x, y, -1);
            }
        }
        mSegments = new Vector<Segment>();
        mSegmentIdsToRemove = new Vector<Integer>();
    }

    public void segmentate() {
        Log.d(LOG_TAG, ">> segmentate()");
        int segId = -1;
        for (int y = 0; y < h; ++y) {
            for (int x = 0; x < w; ++x) {
                if (mAllPixelsBoolVals[x][y]) { // only the important pixels are created others are null in mAllPixelsCoords table
                    Coord c = new Coord(x, y, -1);
                    if (x >= 1 && mAllPixelsCoords[x - 1][y].mOwnerId >= 0) {
                        // left pixel is already in segment so this one should belong to that segment too
                        if (y >= 1 && mAllPixelsCoords[x][y - 1].mOwnerId >= 0
                                && mAllPixelsCoords[x][y - 1].mOwnerId != mAllPixelsCoords[x - 1][y].mOwnerId) {
                            // but if the upper pixel is in different than current left then upper segment has priority
                            // and what is more we need to combine all pixels from (segment id) == (left pixel segment id) into
                            // upper pixel segment
                            // add pixel to upper segment
                            int upVal = mAllPixelsCoords[x][y - 1].mOwnerId;
                            int leftVal = mAllPixelsCoords[x - 1][y].mOwnerId;
                            int addToThisId = Math.min(upVal, leftVal);
                            int removeThisId = Math.max(upVal, leftVal);
                            c.mOwnerId = addToThisId;
                            Segment addItemsTo = mSegments.elementAt(addToThisId);
                            addItemsTo.addItem(c);// add this pixel to upper segment
                            Segment toBeRemoved = mSegments.elementAt(removeThisId);
                            for (int i = 0; i < toBeRemoved.getPixels().size(); ++i) {
                                Coord ic = toBeRemoved.getPixels().elementAt(i);
                                // for each pixel in left segment move it to upper segment
                                ic.mOwnerId = c.mOwnerId;
                                mAllPixelsCoords[ic.x][ic.y].mOwnerId = c.mOwnerId;
                                addItemsTo.addItem(ic);
                            }
                            mSegmentIdsToRemove.add(new Integer(removeThisId));
                            // mSegments.remove(toBeRemoved);
                            // --segId; // because we will combine last segment into earlier one
                        } else {
                            c.mOwnerId = mAllPixelsCoords[x - 1][y].mOwnerId;
                            mSegments.elementAt(c.mOwnerId).addItem(c);// add this pixel to segment
                        }
                    } else if (y >= 1 && mAllPixelsCoords[x][y - 1].mOwnerId >= 0) {
                        // upper pixel is already in segment so this one should belong to that segment too
                        c.mOwnerId = mAllPixelsCoords[x][y - 1].mOwnerId;
                        mSegments.elementAt(c.mOwnerId).addItem(c);// add this pixel to segment
                    } else {
                        ++segId;
                        Segment s = new Segment(segId);
                        c.mOwnerId = segId;
                        s.addItem(c);
                        mSegments.add(s);
                    }
                    mAllPixelsCoords[x][y] = c;
                }
            }
        }
        // clear unused segments
        Collections.sort(mSegmentIdsToRemove);
        for (int i = 0; i < mSegmentIdsToRemove.size(); ++i) {
            // Log.d(LOG_TAG, "segment remove = " + (mSegmentIdsToRemove.elementAt(i)));
            mSegments.remove(mSegmentIdsToRemove.elementAt(i) - i);
        }
        Log.d(LOG_TAG, "segmentsCount = " + mSegments.size());
    }

    public Vector<Segment> getSegments() {
        return mSegments;
    }

    public void removeSmallSegments(int pixelsCount, boolean[][] pic) {
        Vector<Integer> segIds = new Vector<Integer>();
        for (Iterator<Segment> iterator = mSegments.iterator(); iterator.hasNext();) {
            Segment seg = iterator.next();
            if (seg.getSegmentSize() < pixelsCount) {
                segIds.add(new Integer(seg.getSegId()));
            }
        }
        for (Iterator<Integer> iterator = segIds.iterator(); iterator.hasNext();) {
            removeSegment(iterator.next().intValue(), pic);
        }
    }

    private void removeSegment(int segId, boolean[][] pic) {
        for (Iterator<Segment> it = mSegments.iterator(); it.hasNext();) {
            Segment seg = it.next();
            if (seg.getSegId() == segId) {
                Vector<Coord> px = seg.getPixels();
                for (Iterator<Coord> iterator2 = px.iterator(); iterator2.hasNext();) {
                    Coord coord = iterator2.next();
                    pic[coord.x][coord.y] = false; // remove pixel from source bitmap
                }
                it.remove();
                break;
            }
        }
    }
}
